#ifndef OC_TDTILES_H_
#define OC_TDTILES_H_

#include "Osg2CesiumDef.h"
#include "oe/optional.h"
#include "oe/URI.h"
#include "OCesium/Matrix4.h"

namespace OCPP {
    namespace Cesium
    {
        enum RefinePolicy
        {
            REFINE_REPLACE,
            REFINE_ADD
        };

        struct LoadContext
        {
            URIContext _uc;
            RefinePolicy _defaultRefine;
        };

        class _Osg2CesiumExport Asset
        {
            OE_OPTION(std::string, version);
            OE_OPTION(std::string, tilesetVersion);
            OE_OPTION(std::string, gltfUpAxis);

            Asset() { }
            Asset(const Json::Value& value) { fromJSON(value); }
            void fromJSON(const Json::Value&);
            Json::Value getJSON() const;
        };

        class _Osg2CesiumExport BoundingVolume
        {
            OE_OPTION(osg::BoundingBox, box);
            OE_OPTION(osg::BoundingBox, region);
            OE_OPTION(osg::BoundingSphere, sphere);

            BoundingVolume() { }
            BoundingVolume(const Json::Value& value) { fromJSON(value); }
            void fromJSON(const Json::Value&);
            Json::Value getJSON() const;

            osg::BoundingSphere asBoundingSphere() const;
        };

        class _Osg2CesiumExport TileContent
        {
            OE_OPTION(BoundingVolume, boundingVolume);
            OE_OPTION(URI, uri);

            TileContent() { }
            TileContent(const Json::Value& value, LoadContext& uc) { fromJSON(value, uc); }
            void fromJSON(const Json::Value&, LoadContext&);
            Json::Value getJSON() const;
        };

        class _Osg2CesiumExport Tile : public osg::Referenced
        {
        public:
            OE_OPTION(BoundingVolume, boundingVolume);
            OE_OPTION(BoundingVolume, viewerRequestVolume);
            OE_OPTION(double, geometricError);
            OE_OPTION(RefinePolicy, refine);
            OE_OPTION(Matrix4, transform);
            OE_OPTION(TileContent, content);
            OE_OPTION_VECTOR(osg::ref_ptr<Tile>, children);

            Tile() : _refine(REFINE_ADD) { }
            Tile(const Json::Value& value, LoadContext& uc) { fromJSON(value, uc); }
            void fromJSON(const Json::Value&, LoadContext& uc);
            Json::Value getJSON() const;
        };

        class _Osg2CesiumExport Tileset : public osg::Referenced
        {
        public:
            OE_OPTION(Asset, asset);
            OE_OPTION(BoundingVolume, boundingVolume);
            //todo: properties
            OE_OPTION(double, geometricError);
            OE_OPTION_REFPTR(Tile, root);

            Tileset() { }
            Tileset(const Json::Value& value, LoadContext& uc) { fromJSON(value, uc); }
            void fromJSON(const Json::Value&, LoadContext& uc);
            Json::Value getJSON() const;

            static Tileset* create(const std::string& tilesetJSON, const URIContext& uc);
        };
    }
}

inline OCPP::Cesium::Tileset* _OC_NewTileset()
{
    OCPP::Cesium::Tileset* tileset = new OCPP::Cesium::Tileset;
    tileset->asset()->version() = "1.0";
    tileset->asset()->tilesetVersion() = "1.0.1-OCPPto3dtiles";
    tileset->asset()->gltfUpAxis() = "Z";
    tileset->root() = new OCPP::Cesium::Tile;
    return tileset;
}

inline void _OC_WriteTileset(OCPP::Cesium::Tileset* tileset, OCPP::String filename)
{
    std::ofstream fout(filename.c_str());
    Json::Value json = tileset->getJSON();
    Json::StyledStreamWriter writer;
    writer.write(fout, json);
    fout.close();
}

#endif //
